#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# @Time    : 2024/10/27 10:14
# @Author  : astock
# @File    : gen_ft.py
# @Software: PyCharm
import collections
import os
import sys
import threading
import time
import traceback
from queue import Queue

import pandas
import pandas as pd
from loguru import logger

from models.stock_model import StockNumber, DayInfo
from mylib import download_all
from mylib.mycsv import sort_csv
from send_email import send_email_html_gen_low_rank_old
from update_sh import get_bkd_today, get_sh_down_date

st_queue = Queue()


def get_all_stock(down_times, n, bkd, stocks):
    """

    :param down_times:
    :param n:
    :param bkd:
    :param stocks:
    :return:
    """
    today_date = get_bkd_today(bkd)
    dname = os.path.basename(__file__).split('.')[0]
    log_dir = f'result_{dname}'
    if not os.path.exists(log_dir):
        os.makedirs(log_dir)
    csv_path = f'{log_dir}/{today_date}_{dname}.csv'
    send_email = False
    with open(csv_path, 'w') as fw:
        fw.write(f'代码,名称,行业,日期,今日涨幅,最高,价格,最低,谷底日期,谷底价,谷底跌幅,谷底跌幅_pre,down_times,link\n')
    df = pd.read_csv('all.csv')
    for row in df.index:
        sn = StockNumber(df.loc[row])
        if stocks and sn.name not in stocks:
            continue
        if '退' in sn.name:
            continue
        if 'ST' in sn.name:
            continue
        if not str(sn.ts_code).startswith('60') \
                and not str(sn.ts_code).startswith('30') \
                and not str(sn.ts_code).startswith('00'):
            continue
        try:
            min_pct, pre_min_pct, today_di, min_di, link_addr = analysis_stock(sn, today_date, n)
            if today_di is None and min_di is None:
                # 不满足条件，计算下一个
                continue
            t_pct = today_di.pct_chg
            t_high = today_di.high
            t_close = today_di.close
            t_low = today_di.low
            link_addr = f'=HYPERLINK("{link_addr}")'
            logger.info(f'bkd={bkd},{today_date},{link_addr},{t_close},{sn}')
            with open(csv_path, 'a+') as fw:
                fw.write(
                    f'{sn.ts_code},{sn.name},{sn.industry},{today_date},{t_pct},{t_high},{t_close},{t_low},{min_di.trade_date},{min_di.low},{min_pct},{pre_min_pct},{down_times},{link_addr}\n')
            send_email = True
        except Exception as e:
            logger.error(e)
            logger.error(traceback.format_exc())
    # 发邮件
    if send_email:
        sort_csv(csv_path, ['谷底跌幅'], [False])
        sdf = pandas.read_csv(csv_path)
        all_cnt = sdf.shape[0]
        new_csv_path = csv_path.replace('.csv', f'_{all_cnt}.csv')
        os.rename(csv_path, new_csv_path)
        # if all_cnt and bkd == 0:
        if all_cnt:
            email_list = sdf.to_dict(orient='records')
            max_stock_name = email_list[0].get('名称')
            gddf = round(email_list[0].get('谷底跌幅') * 100, 2)
            max_df = f'{gddf}%'
            first_stock_name = f'{max_stock_name}{max_df}'
            if down_times > 0:
                down_times_str = f'{down_times}连涨'
            else:
                down_times_str = f'{down_times}连跌'
            msg_title = f'上证{down_times_str}-{os.path.basename(csv_path)}【{all_cnt}】-【{first_stock_name}】bkd={bkd}_谷底跌幅_gen_low_rank_old'.replace(
                '.csv',
                '')
            st_queue.put((1, bkd, email_list, msg_title))


def analysis_stock(sn, p_today_date, n):
    """

    :param sn:
    :param p_today_date: 计算日期
    :param n: 计算左右N日顶
    :return:
    """
    sn_csv_path = f'stocks/{sn.ts_code}.csv'
    if not os.path.exists(os.path.abspath(sn_csv_path)):
        download_all.analysis_stock(sn)
    df2 = pd.read_csv(sn_csv_path)
    low_arr = []  # 存储每日最低价数组
    min_di_arr = []  # 谷底对象数组
    stock_number_arr = sn.ts_code.split('.')
    stock_number = f'{stock_number_arr[1]}{stock_number_arr[0]}'
    link_addr = f'https://xueqiu.com/S/{stock_number}'
    today_di = None
    preday_di = None
    start = False
    for row2 in df2.index:
        di = DayInfo(sn, df2.iloc[row2])
        if di.trade_date > p_today_date:
            continue
        if di.trade_date == p_today_date:
            start = True
        if di.trade_date < p_today_date and not start:
            # 计算历史时候，遇到停牌空档期这种，不计算
            break
        di.name = sn.name
        di.ts_code = sn.ts_code
        di.industry = sn.industry
        if di.trade_date == p_today_date:
            today_di = di
            continue
        if preday_di is None and today_di is not None:
            preday_di = di
        # 只获取最近5次最低支撑点
        if len(min_di_arr) == 1:
            min_low_arr = [round(di.low, 2) for di in min_di_arr]
            # 今日最低价与最近一次谷底差价
            if min_low_arr[0] <= today_di.low:
                # 还没跌破最低，这种情况不要
                break
            else:
                # 计算今日最低价，与谷底的跌幅百分比
                min_pct = round((min_low_arr[0] - today_di.low) / today_di.low, 4)
                pre_min_pct = round((min_low_arr[0] - preday_di.low) / preday_di.low, 4)
                return min_pct, pre_min_pct, today_di, min_di_arr[0], link_addr
        low_arr.append(di.low)
        if len(low_arr) == n * 2 + 1:
            if min(low_arr) == low_arr[n]:
                min_di = DayInfo(sn, df2.loc[row2 - n])
                min_di_arr.append(min_di)
            low_arr.pop(0)
    return None, None, None, None, None


def t_cal(start_bkd, end_bkd):
    sh_dict, date_arr = get_sh_down_date()
    for bkd, d in enumerate(date_arr):
        if bkd < start_bkd:
            continue
        if bkd >= end_bkd:
            break
        down_times = 0
        cnt = -1
        while 1:
            cnt += 1
            d_key = date_arr[bkd + cnt]
            sh_item = sh_dict.get(d_key)
            zdf = sh_item.get('涨跌幅')
            if down_times > 0 and zdf < 0:
                break
            if down_times < 0 and zdf > 0:
                break
            if zdf < 0:
                down_times -= 1
                continue
            if zdf > 0:
                down_times += 1
                continue

        # if down_times < max_down:
        #     continue

        n_arr = [5]
        for n in n_arr:
            try:
                get_all_stock(down_times, n, bkd, stocks)
            except Exception as e:
                print(e)

    st_queue.put((0, None, None, None))


def t_send():
    while 1:
        time.sleep(10)
        if not st_queue:
            continue
        a, bkd, email_list, msg_title = st_queue.get()
        if a:
            send_email_html_gen_low_rank_old.send_html(email_list, msg_title)
            time.sleep(60)
            # cal indus
            low_indus_set = set()
            for index, item in enumerate(email_list):
                low_pct = round(item.get('谷底跌幅') * 100, 2)
                if low_pct < 5 or index > 10:
                    break
                ind = item.get('行业')
                low_indus_set.add(ind)
        else:
            # 计算完成
            break


def run(start_bkd, bkd):
    # t_cal(start_bkd, bkd)
    t1 = threading.Thread(target=t_cal, args=(start_bkd, bkd,))
    t2 = threading.Thread(target=t_send, args=())
    t1.start()
    t2.start()
    t1.join()
    t2.join()


if __name__ == '__main__':
    """
    当前价，低于最近一个5日底，排名
    """
    stocks = [
        # '晨丰科技'
        # '中成股份',
        # '通威股份',
        # '中天科技',
        # '均胜电子',
        # '长江电力',
        # '隆基绿能',
        # '东方电子',
        # '*ST龙宇',
    ]
    start_bkd = 0
    try:
        arguments = sys.argv[1:]
        if arguments:
            bkd = int(arguments[0])
            start_bkd = int(arguments[1]) if len(arguments) == 2 else start_bkd
        else:
            bkd = 1
    except Exception as e:
        bkd = 1
        logger.error(e)
    # 开始计算
    start_bkd = bkd - 1 if not start_bkd else start_bkd
    run(start_bkd, bkd)
